package bai.publicwelfare_backend.service.impl.user;



import bai.publicwelfare_backend.common.utils.JwtUtil;
import bai.publicwelfare_backend.exception.BusinessException;
import bai.publicwelfare_backend.exception.ErrorCode;
import bai.publicwelfare_backend.mapper.user.UserMapper;
import bai.publicwelfare_backend.pojo.dto.user.ChangeUserDTO;
import bai.publicwelfare_backend.pojo.dto.user.LoginDTO;
import bai.publicwelfare_backend.pojo.dto.user.RegisterDTO;
import bai.publicwelfare_backend.pojo.entity.user.User;
import bai.publicwelfare_backend.pojo.vo.user.AuthVO;
import bai.publicwelfare_backend.service.user.UserServiceI;
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.ZoneId;
import java.util.concurrent.TimeUnit;


@Service
@RequiredArgsConstructor
public class UserService implements UserServiceI {

    // 获取日志记录器
    private static final Logger log = LogManager.getLogger(JwtUtil.class); // 修复日志记录器

    private final JwtUtil jwtUtil;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PasswordEncoder passwordEncoder; // BCrypt密码加密
    @Override
    public AuthVO register(RegisterDTO dto) {
        // 检查用户名是否已存在（原有逻辑不变）
        User existingUser = userMapper.selectByUsername(dto.getUsername());
        if (existingUser != null) {
            log.warn("登录失败：用户名已存在 {}", dto.getUsername());
            throw new BusinessException(ErrorCode.USERNAME_EXISTS, "用户名已存在");
        }

        // 创建新用户（新增tokenVersion初始化）
        User user = new User();
        BeanUtils.copyProperties(dto, user);
        user.setPassword(passwordEncoder.encode(dto.getPassword()));
        user.setTokenVersion(0); // 重要！初始化为0

        userMapper.insert(user);

        // ===== 双Token生成 =====
        String accessToken = jwtUtil.generateToken(user.getId(), 0, 1, TimeUnit.DAYS, "accessToken");  // 2小时过期
        String refreshToken = jwtUtil.generateToken(user.getId(), 0, 7, TimeUnit.DAYS, "refreshToken"); // 7天过期

        // 存储RefreshToken到Redis（关键步骤！）
        redisTemplate.opsForValue().set(
                "refresh:" + user.getId(), // 键
                refreshToken, // 刷新令牌
                1, // 过期时间数值
                TimeUnit.MINUTES // 过期时间单位
        );

        // 返回双Token
        return new AuthVO(
                accessToken,
                refreshToken,
                user.getUsername(),
                user.getId(),
                jwtUtil.getExpiryDate(accessToken).toInstant()
                        .atZone(ZoneId.systemDefault())
                        .toLocalDateTime() // 从 UTC 时间转换为本地时间
        );
    }

    @Override
    public void changePassword(Long userId, String newPassword) {
        // 1. 更新密码
        userMapper.updatePassword(userId, passwordEncoder.encode(newPassword));

        // 2. 递增tokenVersion使旧Token失效（关键！）
        userMapper.incrementTokenVersion(userId);

        // 3. 删除Redis中的RefreshToken
        redisTemplate.delete("refresh:" + userId);
    }

    @Override
    public AuthVO login(LoginDTO dto) {

        // ===== 0. 先验证验证码（新增步骤）=====


        // ===== 1. 登录限流检查（优先执行）=====
        String limitKey = "login:limit:" + dto.getUsername();
        Long attempts = redisTemplate.opsForValue().increment(limitKey);

        // 首次尝试时设置过期时间
        if (attempts == 1) {
            redisTemplate.expire(limitKey, 1, TimeUnit.HOURS); // 1小时内限流
        }

        // 超过阈值直接拒绝
        if (attempts != null && attempts >= 10) {
            Long remainSeconds = redisTemplate.getExpire(limitKey, TimeUnit.SECONDS);
            log.warn("登录限流触发：username={}, attempts={}", dto.getUsername(), attempts);
            throw new BusinessException(ErrorCode.LOGIN_LIMIT,
                    "登录失败次数过多，请 " + (remainSeconds/60) + " 分钟后再试");
        }

        // 2. 查询用户
        User user = userMapper.selectByUsername(dto.getUsername());
        if (user == null) {
            log.warn("登录失败：用户名不存在 {}", dto.getUsername()); // 日志记录真实原因
            throw new BusinessException(ErrorCode.PASSWORD_ERROR); // 对外模糊提示
        }

        // 3. 验证密码（核心！）
        if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) {
            log.warn("登录失败：密码错误 {}", dto.getPassword());
            throw new BusinessException(ErrorCode.PASSWORD_ERROR);
        }

        // 4. 更新Token版本号（确保后续Token使用新版本）
            userMapper.incrementTokenVersion(user.getId());

        // 5. 重新获取最新用户信息（含新tokenVersion）
            user = userMapper.selectByUsername(dto.getUsername());


        // 4. 生成双Token（和注册逻辑保持一致）
        String accessToken = jwtUtil.generateToken(user.getId(), user.getTokenVersion(), 1, TimeUnit.DAYS, "accessToken");
        String refreshToken = jwtUtil.generateToken(user.getId(), user.getTokenVersion(), 7, TimeUnit.DAYS, "refreshToken");


        // 5. 存储Token到Redis（覆盖旧的）
        redisTemplate.opsForValue().set(
                "accessToken:" + user.getId(),
                accessToken,
                1,
                TimeUnit.DAYS
        );
        redisTemplate.opsForValue().set(
                "refresh:" + user.getId(),
                refreshToken,
                7,
                TimeUnit.DAYS
        );

        // 7. 返回AuthVO（和注册返回结构一致）
        return new AuthVO(
                accessToken,
                refreshToken,
                user.getUsername(),
                user.getId(),
                jwtUtil.getExpiryDate(accessToken).toInstant()
                        .atZone(ZoneId.systemDefault())
                        .toLocalDateTime()
        );
    }
//    退出登录
    @Override
    public void logout(String token) {
        // 1. 获取当前用户及Token
        Long userId = jwtUtil.getUserIdFromToken(token);
        // 2. 加入黑名单（剩余有效期）
        long ttl = jwtUtil.getRemainingSeconds(token);
        if (ttl > 0) {
            redisTemplate.opsForValue().set(
                    "blacklist:" + token,
                    "revoked",
                    ttl, TimeUnit.SECONDS
            );
        }
        // 3. 删除RefreshToken
        redisTemplate.delete("refresh:" + userId);
        // 4. 更新Token版本号（强制全局退出）
        userMapper.incrementTokenVersion(userId);
        return ;
    }

    @Override
    public User getUserInfo(String token) {
        // 1. 获取当前用户及Token
        Long userId = jwtUtil.getUserIdFromToken(token);
        User user = userMapper.selectById(userId);
        return user;
    }

    @Override
    public void setUserName(String accessToken, ChangeUserDTO setUserName) {
        // 1. 验证accessToken是否有效
        User user = userMapper.selectById(Long.valueOf(setUserName.getId()));
        if (user == null) {
            throw new BusinessException(ErrorCode.USER_NOT_FOUND);
        }
        // 2. 验证token是否有效且与用户匹配
        if (!jwtUtil.validateToken(accessToken, user.getTokenVersion())) {
            throw new BusinessException(ErrorCode.TOKEN_INVALID);
        }
        // 3. 验证token中的userId是否与请求修改的用户ID一致
        Long tokenUserId = jwtUtil.getUserIdFromToken(accessToken);
        if (!tokenUserId.equals(setUserName.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "只能修改自己的用户名");
        }
        // 4. 检查新用户名是否已存在
        User existingUser = userMapper.selectByUsername(setUserName.getUserName());
        if (existingUser != null && !existingUser.getId().equals(setUserName.getId())) {
            throw new BusinessException(ErrorCode.USERNAME_EXISTS);
        }

        // 5. 更新用户名
        userMapper.setUserName(setUserName);

    }
}
